package files

import (
	"fmt"
	"io"
	"io/fs"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
)

// 封装 stat 结构
type stat struct {
	fs.FileInfo

	dir      string
	ext      string
	filename string
}

// 获取文件所在目录
func (s stat) Dir() string {
	return s.dir
}

// 获取文件名扩展
func (s stat) Ext() string {
	return s.ext
}

// 获取文件名, 不带扩展
func (s stat) Filename() string {
	if s.filename == "" {
		s.filename = strings.TrimSuffix(s.Name(), s.ext)
	}
	return s.filename
}

/*
 * 获取文件信息
 *   filename: 文件名
 * returns: 写入的字符数，错误信息
 */
func GetStat(filename string, extensions ...string) (stat, error) {
	// 读取文件信息
	fileInfo, err := os.Stat(filename)
	if err != nil {
		return stat{}, err
	}

	return stat{
		FileInfo: fileInfo,
		dir:      path.Dir(filename),
		ext:      GetExtension(filename, extensions...),
	}, nil
}

/*
 * 获取文件扩展名
 * filename: 文件名
 */
func GetExtension(filename string, extensions ...string) string {
	if len(extensions) > 0 {
		for _, ext := range extensions {
			if strings.HasSuffix(filename, ext) {
				return ext
			}
		}
	}
	return path.Ext(filename)
}

/*
 * 获取文件名， 不带扩展
 * filename: 文件名
 * extensions: 文件扩展列表
 */
func GetName(filename string, extensions ...string) string {
	baseName := path.Base(filename)
	extension := GetExtension(filename, extensions...)
	return strings.TrimSuffix(baseName, extension)
}

/*
 * 读取文件内容
 * filename: 文件路径
 */
func ReadFile(filename string) (content []byte, err error) {
	// 打开写入文件
	df, err := os.Open(filename)
	if err != nil {
		return
	}
	defer df.Close()

	// 创建缓冲区，边读边写
	buf := make([]byte, 4096) // 由于虚拟内存的最小单位是 page(默认是4096)，所以，设置成 4096
	readSize := 0
	for {
		readSize, err = df.Read(buf)
		if err != nil && err != io.EOF { // 读取错误
			return
		}
		if readSize == 0 { // 读完毕
			err = nil
			return
		}
		content = append(content, buf[:readSize]...)
	}
}

/*
 * 复制文件
 * src: 复制源文件
 * dist: 目标文件
 * ignores : 忽略文件或文件夹
 */
func CopyFile(src, dist string) (copySize int, err error) {
	// 打开源文件
	sFile, err := os.Open(src)
	if err != nil {
		return
	}
	defer sFile.Close()

	// 打开写入文件
	dFile, err := os.Create(dist)
	if err != nil {
		return
	}
	defer dFile.Close()

	// 创建缓冲区，边读边写
	buf := make([]byte, 4096) // 由于虚拟内存的最小单位是 page(默认是4096)，所以，设置成 4096
	readSize := 0
	for {
		readSize, err = sFile.Read(buf)
		if err != nil && err != io.EOF { // 读取错误
			return
		}
		if readSize == 0 { // 读完毕
			err = nil
			return
		}
		copySize += readSize
		// 写入文件
		dFile.Write(buf[:readSize])
	}
}

/**
 * 文件按照数字大小来切片并移动
 *   file: 检查的文件名，不带
 * returns: 错误信息
 */
func CutFile(file string, extensions ...string) (string, error) {
	// 获取文件信息
	stat, err := GetStat(file, extensions...)
	if err != nil {
		return "", err
	}
	// 读取当前目录下的文件
	entries, err := os.ReadDir(stat.Dir())
	if err != nil {
		return "", fmt.Errorf("Read dir error: %v", err)
	}
	var maxNum int = 0
	// 正则表达式，匹配文件名末尾数字
	re := regexp.MustCompile("^" + stat.Filename() + `_(\d+)` + stat.Ext() + "$")
	for _, entry := range entries {
		if entry.IsDir() {
			// 跳过目录
			continue
		}
		// 匹配文件数字
		matches := re.FindStringSubmatch(entry.Name())
		if len(matches) == 0 {
			// 不匹配的文件
			continue
		}
		num, _ := strconv.Atoi(matches[1]) // 将数字字符串转换为整数
		if num > maxNum {
			maxNum = num
		}
	}
	// 计算切片文件名
	newFilename := path.Join(stat.Dir(), fmt.Sprintf("%s_%04d%s", stat.Filename(), maxNum+1, stat.Ext()))
	// 文件移动(重命名)
	err = Rename(file, newFilename)
	if err != nil {
		return "", fmt.Errorf("Rename file error: %v", err)
	}
	return path.Join(stat.Dir(), newFilename), nil
}

/*
 * 打开文件句柄(如果文件存在, 内容会被清空)
 *   filename: 文件名
 * returns: 文件句柄, 错误信息
 */
func Create(filename string) (*os.File, error) {
	return os.Create(filename)
}

/*
 * 删除文件
 *   filename: 文件名
 * returns: 错误信息
 */
func Remove(filename string) error {
	return os.Remove(filename)
}

/*
 * 重命名(移动)文件
 *   oldPath: 原始文件名
 *   newPath: 新文件名
 * returns: 错误信息
 */
func Rename(oldPath string, newPath string) error {
	return os.Rename(oldPath, newPath)
}

/*
 * 打开文件文件句柄
 *   filename: 文件名
 *   content: 要写入的内容
 * returns: 写入的字符数，错误信息
 */
func OpenFile(filename string) (*os.File, error) {
	return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
}

/*
 * 将 []byte 追加到句柄
 *   fp: 写入句柄
 *   content: 要写入的内容
 * returns: 写入的字符数，错误信息
 */
func PutBytesToWriter(fp *os.File, content []byte) (n int, err error) {
	return fp.Write(content)
}

/*
 * 将 string 追加到句柄
 *   fp: 写入句柄
 *   content: 要写入的内容
 * returns: 写入的字符数，错误信息
 */
func PutStringToWriter(fp *os.File, content string) (n int, err error) {
	return fp.WriteString(content)
}

/*
 * 将 []byte 追加文件
 *   fp: 写入句柄
 *   content: 要写入的内容
 * returns: 写入的字符数，错误信息
 */
func PutBytesToFile(filename string, content []byte) (int, error) {
	if fp, err := OpenFile(filename); err != nil {
		return 0, err
	} else {
		defer fp.Close()
		return PutBytesToWriter(fp, content)
	}
}

/*
 * 将 string 追加文件
 *   fp: 写入句柄
 *   content: 要写入的内容
 * returns: 写入的字符数，错误信息
 */
func PutStringToFile(filename string, content string) (n int, err error) {
	if fp, err := OpenFile(filename); err != nil {
		return 0, err
	} else {
		defer fp.Close()
		return PutStringToWriter(fp, content)
	}
}
